package com.stox.shared.charting.model;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import com.stox.shared.charting.model.Pnf.Box;
import com.stox.shared.core.model.Bar;

public class Pnf extends Bar {

	public static class Box extends Bar {

		private final boolean up;
		private final double high;
		private final double low;
		private final double size;
		private final double reversalSize;
		private final List<Bar> bars = new ArrayList<Bar>();

		public Box(final boolean up, final double size, final double reversalSize, final double high, final double low, final Bar initialBar) {
			this.up = up;
			this.size = size;
			this.reversalSize = reversalSize;
			this.high = high;
			this.low = low;
			doConsume(initialBar);
		}

		public Box(final boolean up, final double size, final double reversalSize, final double value, final Bar initialBar) {
			this.up = up;
			this.size = size;
			this.reversalSize = reversalSize;
			high = value + (size - (value % size));
			low = value - (value % size);
			doConsume(initialBar);
		}

		public int compareTo(final Box o) {
			return null == o ? 1 : (high > o.high ? 1 : (high == o.high ? 0 : -1));
		}

		public Box consume(final Bar bar) {
			final double value = bar.getClose();
			if (up && (value >= (high + size))) {
				return new Box(up, size, reversalSize, high + size, high, bar);
			} else if (up && (value <= (high - reversalSize - size))) {
				return new Box(!up, size, reversalSize, high - reversalSize, high - reversalSize - size, bar);
			} else if (!up && (value <= (low - size))) {
				return new Box(up, size, reversalSize, low, low - size, bar);
			} else if (!up && (value >= (low + reversalSize + size))) {
				return new Box(!up, size, reversalSize, low + reversalSize + size, low + reversalSize, bar);
			} else {
				doConsume(bar);
				return this;
			}
		}

		private void doConsume(final Bar bar) {
			final double value = bar.getClose();
			// if ((value <= high) && (value >= low)) {
			if (0 == getOpen()) {
				setOpen(value);
			}
			setClose(value);
			// }
			setVolume(getVolume() + bar.getVolume());
			setDate(bar.getDate());
			setScrip(bar.getScrip());
			setScripId(bar.getScripId());
			bars.add(0, bar);
		}

		@Override
		public double getHigh() {
			return high;
		}

		@Override
		public double getLow() {
			return low;
		}

		@Override
		public void setHigh(final double high) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setLow(final double low) {
			throw new UnsupportedOperationException();
		}

		public double getSize() {
			return size;
		}

		public double getReversalSize() {
			return reversalSize;
		}

		public boolean isUp() {
			return up;
		}
	}

	private final boolean up;
	private final double size;
	private final double reversalSize;
	private final List<Box> boxes = new ArrayList<Box>();

	public Pnf(final boolean up, final double size, final double reversalSize, final Date initialDate) {
		this.up = up;
		this.size = size;
		this.reversalSize = reversalSize;
		setDate(initialDate);
		setHigh(Double.MIN_VALUE);
		setLow(Double.MAX_VALUE);
	}

	public boolean isUp() {
		return up;
	}

	public List<Box> getBoxes() {
		return boxes;
	}

	public double getSize() {
		return size;
	}

	public double getReversalSize() {
		return reversalSize;
	}

	public void doConsume(final Box box) {
		if (!boxes.contains(box)) {
			if (0 == getOpen()) {
				setOpen(box.getOpen());
			}
			setHigh(Math.max(getHigh(), box.getHigh()));
			setLow(Math.min(getLow(), box.getLow()));
			setClose(box.getClose());
			setDate(box.getDate());
			setVolume(getVolume() + box.getVolume());
			setScrip(box.getScrip());
			setScripId(box.getScripId());
			boxes.add(0, box);
		}
	}


}


class BoxComparator implements Comparator<Box> {
	@Override
	public int compare(final Box o1, final Box o2) {
		if ((null == o1) && (null == o2)) {
			return 0;
		} else if ((null != o1) && (null == o2)) {
			return 1;
		} else if ((null == o1) && (null != o2)) {
			return -1;
		} else {
			return o1.getHigh() == o2.getHigh() ? 0 : (o1.getHigh() > o2.getHigh() ? 1 : -1);
		}
	}
}
